<!doctype html>
<html>
    <head>
        <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no" />
        <link rel="stylesheet" crossorigin="anonymous" href="/node_modules/@perspective-dev/viewer/dist/css/pro.css" />
        <link rel="stylesheet" crossorigin="anonymous" href="/node_modules/@perspective-dev/workspace/dist/css/pro.css" />
        <style>
            perspective-workspace {
                flex: 1;
                overflow: visible;
                border: 1px solid #666;
                overflow: hidden;
            }
            body {
                overflow: hidden;
            }

            #app {
                display: flex;
                flex-direction: column;
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                background-color: #f2f4f6;
            }

            #controls {
                display: flex;
                padding: 12px;
                gap: 12px;
            }

            .range {
                position: relative;
                display: inline-flex;
                flex-direction: column;
                gap: 6px;
            }

            span,
            input,
            button {
                font-family: "ui-monospace", "SFMono-Regular", "SF Mono", "Menlo", "Consolas", "Liberation Mono", monospace;
            }

            span {
                font-size: 10px;
            }
        </style>
    </head>
    <body>
        <div id="app">
            <div id="controls">
                <button id="run">Generate</button>
                <button id="del">Delete</button>
                <div class="range">
                    <span>Rows</span>
                    <input id="num_rows" min="25" max="1000000" type="number" placeholder="#" value="100000" />
                    <span>Update batches</span>
                    <input id="num_batches" min="1" max="100" type="number" placeholder="#" value="1000" />
                    <span>Update delay (ms)</span>
                    <input id="batch_delay" min="100" max="10000" type="number" placeholder="#" value="200" />
                </div>
                <div class="range">
                    <span>Float columns</span>
                    <input id="num_float" min="0" max="50" type="number" placeholder="#" value="5" />
                </div>
                <div class="range">
                    <span>Integer columns</span>
                    <input id="num_integer" min="0" max="50" type="number" placeholder="#" value="5" />
                </div>
                <div class="range">
                    <span>String columns</span>
                    <input id="num_string" min="0" max="50" type="number" placeholder="#" value="5" />
                    <span>Dictionary size</span>
                    <input id="num_strings" min="1" max="500" type="number" placeholder="Unique Strings" value="50" />
                </div>
                <div class="range">
                    <span>Datetime columns</span>
                    <input id="num_datetime" min="0" max="50" type="number" placeholder="#" value="5" />
                </div>
                <div class="range">
                    <span>Bool columns</span>
                    <input id="num_boolean" min="0" max="50" type="number" placeholder="#" value="1" />
                </div>
            </div>
            <perspective-workspace id="psp_workspace">
                <!-- <perspective-viewer editable id="viewer"></perspective-viewer>
            <perspective-viewer editable id="stats"></perspective-viewer> -->
            </perspective-workspace>
        </div>

        <script type="module">
            import "/node_modules/@perspective-dev/viewer-datagrid/dist/cdn/perspective-viewer-datagrid.js";
            import "/node_modules/@perspective-dev/viewer-d3fc/dist/cdn/perspective-viewer-d3fc.js";
            import "/node_modules/@perspective-dev/viewer/dist/cdn/perspective-viewer.js";
            import "/node_modules/@perspective-dev/workspace/dist/cdn/perspective-workspace.js";
            import perspective from "/node_modules/@perspective-dev/client/dist/cdn/perspective.js";

            const client = await perspective.worker();

            const choose = (x) => x[Math.floor(Math.random() * x.length)];
            const range = (x, y) => Math.random() * (y - x) + x;
            const rand_string = () => Math.random().toString(36).substring(7);
            const strings = (choices) => new Array(choices).fill(null).map(rand_string);
            const colname = (name, x) => `${name.charAt(0).toUpperCase() + name.slice(1)} ${x}`;

            function* col_iter() {
                for (const name of ["float", "integer", "string", "datetime", "boolean"]) {
                    const ncols = window[`num_${name}`].value;
                    for (let x = 0; x < ncols; x++) {
                        yield [name, x];
                    }
                }
            }

            const assign = (fn) => {
                const obj = {};
                for (const [name, x] of col_iter()) {
                    obj[colname(name, x)] = fn(name, x);
                }
                return obj;
            };

            const new_schema = () => assign((x) => x);

            let string_cache;
            function reset_strings_cache() {
                string_cache = {};
            }

            reset_strings_cache();

            const get_dict = (xs) => {
                string_cache[xs] = string_cache[xs] || strings(parseInt(num_strings.value));
                return string_cache[xs];
            };

            const cell_args = {
                float: () => range(-10, 10),
                integer: () => Math.floor(range(-10, 10)),
                string: (xs) => choose(get_dict(xs)),
                datetime: () => new Date(),
                boolean: () => choose([true, false, null]),
            };

            const new_row = () => assign((name, x) => cell_args[name](x));
            const gen_data = async () => {
                reset_strings_cache();
                let nrows = num_rows.value;
                let rows = [];
                const batch_size = Math.floor(nrows / num_batches.value);
                const batch_freq = batch_delay.value;
                const tbl = await client.table(new_schema());
                (function batch() {
                    while (nrows > 0) {
                        rows.push(new_row());
                        nrows--;
                        if (nrows % batch_size === 0) {
                            tbl.update(rows);
                            rows = [];
                            setTimeout(batch, batch_freq);
                            break;
                        }
                    }
                })();

                return tbl;
            };

            // GUI

            const make_run_click_callback = (state) => async () => {
                state.table?.delete?.({ lazy: true });
                state.table = gen_data();
                await window.psp_workspace.addTable("superstore", state.table);
            };

            const make_del_click_callback = (state) => async () => {
                if (state.table) {
                    // await viewer.eject();
                    await window.psp_workspace.removeTable("superstore");
                    await state.table.then((x) => x.delete({ lazy: true }));
                    state.table = undefined;
                }
            };

            const state = {};

            // Main
            run.addEventListener("click", make_run_click_callback(state));
            del.addEventListener("click", make_del_click_callback(state));
            run.dispatchEvent(new Event("click"));

            const stats_table = await client.table(
                {
                    heap_size: "float",
                    used_size: "float",
                    cpu_time: "integer",
                    cpu_time_epoch: "integer",
                    version: "integer",
                    timestamp: "datetime",
                    client_used: "float",
                    client_heap: "float",
                },
                {
                    limit: 2000,
                },
            );

            window.psp_workspace.addTable("stats", stats_table);

            (async function checkmem() {
                await stats_table.update([await client.system_info()]);
                setTimeout(checkmem, 200);
            })();

            window.psp_workspace.restore(await fetch("layout.json").then((x) => x.json()));
        </script>
    </body>
</html>
